#include <malloc.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>

#include "android_utils.h"
#include "com_android_test_jnidemo_JniText.h"
extern "C" { //c++调用c
#include "tinyalsa/net_audio_play.h"
}
//java虚拟机
#define  DEBUG_LOG 1

typedef struct long_time_func_param {
    unsigned char* thread_name;
    int err;
} thread_param_t;

extern "C" JNIEXPORT jstring JNICALL Java_com_android_test_jnidemo_JniText_sayHello
        (JNIEnv *env, jclass type ){
        LOGD("jni-ndk Hello World JNI LOG");
    return env->NewStringUTF("Hello World From JNI!!!!!");
}

/*
D/TAG_JNI (15909): jni-ndk Hello World JNI LOG
D/jni-ndk (15909): the string from JNC C 'Hello World From JNI!!!!!'
D/TAG_JNI (15909): jni-ndk s=1, i=1, l=2193752, f=0.000000, d=1.000000, c=
D/TAG_JNI (15909): jni-ndk c_str:中国
 * */

extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_test
        (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f, jdouble d, jchar c, jboolean z, jbyte b, jstring j_str, jobject jobj1, jobject jobj2, jintArray j_int_arr)
{
    //LOGD("jni-ndk s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c,z=%c,b=%d",s,i,l,f,d,c,z,b);
    LOGD("jni-ndk jshort s=%hd",s);
    LOGD("jni-ndk jint i=%hd",i);
    LOGD("jni-ndk jlong l=%lld",l);
    LOGD("jni-ndk jfloat f=%f",f);
    LOGD("jni-ndk jdouble d=%lf",d);
    LOGD("jni-ndk jchar c=%c",c);
    LOGD("jni-ndk jboolean z=%d",z);
    LOGD("jni-ndk jbyte b=%d",b);

    jint k;
    jint *c_array;
    jint arr_len;
    jboolean j_isCopy;

     //直接拷贝方式
    //1:获取数组长度
    /*
    arr_len = env->GetArrayLength(j_int_arr);
    //2:根据数组长度和数组元素的数据类型申请存放java数组元素的缓冲区
    c_array = (jint*)malloc(sizeof(jint) * arr_len);
    //3:初始化缓冲区
    memset(c_array, 0, sizeof(jint) * arr_len);
    LOGD("jni-ndk arr_len = %d", arr_len);
    //4：拷贝java数组中的所有元素到缓冲区中
    env->GetIntArrayRegion(j_int_arr,0,arr_len,c_array);
    for(k=0; k < arr_len; k++)
    {
        LOGD("jni-ndk arr[%d]=%d",k,c_array[k]);
    }
    //5:释放缓冲区
    free(c_array);
    const char *c_str = NULL;
    c_str = env->GetStringUTFChars(j_str, NULL);
    if(c_str == NULL){
        return;//memory out
    }
    LOGD("jni-ndk c_str:%s\n",(char *)c_str);
    env->ReleaseStringUTFChars(j_str, c_str);
*/

    //获取数组元素指针的方式
    //数组中的元素在内存中可能不是连续的，JVM会复制原始数据到缓冲区，然后返回缓冲区的指针

    c_array = env->GetIntArrayElements(j_int_arr, NULL);
    if(c_array == NULL){
        return;
    }

    arr_len = env->GetArrayLength(j_int_arr);
    LOGD("jni-ndk arr_len = %d", arr_len);
    for(k=0; k < arr_len; k++)
    {
        LOGD("jni-ndk GetIntArrayElements arr[%d]=%d",k,c_array[k]);
    }
    //释放可能复制的缓冲区
    env->ReleaseIntArrayElements(j_int_arr,c_array,0);

//在android 4.4验证有问题
/*
    c_array = (jint *)(env->GetPrimitiveArrayCritical(j_int_arr, (jboolean *)j_isCopy));
    LOGD("jni-ndk j_isCopy=%d",j_isCopy);
    if(c_array == NULL){
        return;
    }

    arr_len = env->GetArrayLength(j_int_arr);
    LOGD("jni-ndk arr_len = %d",arr_len);
    for(k=0; k < arr_len; k++)
    {
        LOGD("jni-ndk GetPrimitiveArrayCritical arr[%d]=%d",k,c_array[k]);
    }
    env->ReleasePrimitiveArrayCritical(j_int_arr,c_array,0);
*/
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_com_android_test_jnidemo_JniText_intInt2DArray
        (JNIEnv *env, jclass obj, jint size)
{
    //jobjectArray start
    jint k;
    jobjectArray  result;
    jclass clsIntArray;
    jint j;
    //1:获得一个Int型二维数组类的引用
    LOGD("jni-ndk clsIntArray");
    clsIntArray = env->FindClass("[I");
    if(clsIntArray == NULL){
        return NULL;
    }

    //2:创建一个数组对象
    //length i :引用数组的成员个数
    //elementClass :引用数组的的成员的类型
    LOGD("jni-ndk NewObjectArray");
    result = env->NewObjectArray(size,clsIntArray,NULL);
    if(result == NULL){
        return NULL;
    }

    for(k = 0; k < size; k++)
    {
        //用来给整型数组填充数据的缓冲区
        jint buff[256];
        //声明一个整型数组
        jintArray  intArr = env->NewIntArray(size);
        if(intArr == NULL){
            return NULL;
        }

        for(j = 0; j  < size; j++)
        {
            buff[j] = k + j;
        }

        env->SetIntArrayRegion(intArr,0, size, buff);
        env->SetObjectArrayElement(result,k,intArr);
        env->DeleteLocalRef(intArr);
    }
    LOGD("jni-ndk 2Array ok");
    return result;

}

extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_calJavaStaticMethod
        (JNIEnv *env, jclass cls)
{
    jclass clazz = NULL;
    jstring str_arg = NULL;
    jmethodID mid_static_method;
    //1:从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
    //clazz = (*env).FindClass("com/android/test/jnidemo/ClassMethod");
    clazz = env->FindClass("com/android/test/jnidemo/ClassMethod");
    if(clazz == NULL){
        return;
    }
    //2.从clazz类中查找callStaticMethod方法
    mid_static_method = env->GetStaticMethodID(clazz,"callStaticMethod", "(Ljava/lang/String;I)V");
    if(mid_static_method == NULL){
        LOGD("jni-ndk 找不到callStaticMethod这个静态方法");
        return;
    }
    //3:调用clazz类的callStaticMethod静态方法
    str_arg = env->NewStringUTF("我是静态方法");
    env->CallStaticVoidMethod(clazz,mid_static_method,str_arg,100);
    //4:删除局部引用
    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(str_arg);
}

extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_callJavaInstaceMethod
        (JNIEnv *env, jclass cls)
{
    jclass clazz = NULL;
    jobject  jobj = NULL;
    jmethodID mid_construct = NULL;
    jmethodID mid_instance = NULL;
    jstring str_arg = NULL;
    //1:从classpath路径下搜索ClassMethod这个类,并返回该类的class对象
    clazz = env->FindClass("com/android/test/jnidemo/ClassMethod");
    if(clazz == NULL){
        LOGD("jni-ndk 找不到com.android.test.jnidemo.ClassMethod 这个类");
        return;
    }
    //2.获取类的默认构造方法ID
    mid_construct = env->GetMethodID(clazz,"<init>", "()V");
    if(mid_construct == NULL){
        LOGD("jni-ndk 找不到默认的构造方法");
        return;
    }
    //3:查找实例方法的id
    mid_instance = env->GetMethodID(clazz,"callInstanceMethod","(Ljava/lang/String;I)V");
    if(mid_instance == NULL){
        LOGD("jni-ndk mid_instance id error");
        return;
    }
    //4:创建该类的实例
    jobj = env->NewObject(clazz,mid_construct);
    if(jobj == NULL){
        LOGD("jni-ndk 在com.android.test.jnidemo.ClassMethod类中找不到callInstanceMethod方法");
        return;
    }
    //5:调用对象的实例方法
    str_arg = env->NewStringUTF("我是实例方法");
    env->CallVoidMethod(jobj,mid_instance,str_arg,200);
    //删除局部引用
    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(jobj);
    env->DeleteLocalRef(str_arg);
}
//c/c++ 修改java静态变量和实例变量
extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_accessInstanceField
        (JNIEnv *env, jclass cls, jobject obj){
    jclass clazz;
    jfieldID fid;
    jstring j_str;
    jstring j_newStr;
    const char *c_str = NULL;
    //1:获取AccessField类的Class引用
    clazz = env->GetObjectClass(obj);
    if(clazz == NULL){
        return;
    }
    //2.获取AcessField类实例变量str的属性ID
    fid = env->GetFieldID(clazz,"str","Ljava/lang/String;");
    if(fid == NULL){
        return;
    }
    //3.获取实例变量str的值
    j_str = (jstring)env->GetObjectField(obj,fid);
     //4. 将unicode编码的java字符串转换为c风格字符串
     c_str = env->GetStringUTFChars(j_str, NULL);
     if(c_str == NULL){
         return;
     }
    LOGD("jni-ndk In c ---> ClassField.str = %s\n", c_str);
     env->ReleaseStringUTFChars(j_str,c_str);
     //5:修改实例变量str的值
     j_newStr = env->NewStringUTF("This is C String");
     if(j_newStr == NULL){
         return;
     }

     env->SetObjectField(obj, fid, j_newStr);
     //6:删除局部引用
     env->DeleteLocalRef(clazz);
     env->DeleteLocalRef(j_str);
     env->DeleteLocalRef(j_newStr);


}

extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_accessStaticField
        (JNIEnv *env, jclass cls){
    jclass  clazz;
    jfieldID fid;
    jint num;
    //1:获取ClassFied类的Class引用
    clazz = env->FindClass("com/android/test/jnidemo/ClassField");
    if(clazz == NULL){
        return;
    }

    //2:获取ClassField类静态变量num的属性ID
    fid = env->GetStaticFieldID(clazz, "num","I");
    if(fid == NULL){
        return;
    }

    //3:获取静态变量num的值
    num = env->GetStaticIntField(clazz,fid);
    LOGD("jni-ndk In C ---> ClassField.num = %d\n", num);
    //4:修改静态变量num的值
    env->SetStaticIntField(clazz,fid,80);
    //删除局部引用
    env->DeleteLocalRef(clazz);

}

extern "C" JNIEXPORT void JNICALL Java_com_android_test_jnidemo_JniText_YUVtoRBG
    (JNIEnv *env, jclass obj, jintArray rgb, jbyteArray yuv420sp, jint width, jint height)
{
    jboolean isCopy;
    jint len;
    jint k;
    //add test ++
    jclass clazz = NULL;
    jmethodID voice_static_method;
    jbyteArray voice_to_java = NULL;
    jshortArray  voice_short_data = NULL;
    jshort voice_short_to_java[] = {0x1111,0x2222,0x3333,0x4444,0x1f9e};
    jint short_size = sizeof(voice_short_to_java)/sizeof(jshort);

    //返回AudioFrame类
    clazz = env->FindClass("com/android/test/jnidemo/AudioFrame");
    if(clazz == NULL){
        LOGD("jni-ndk FindClass com/android/test/jnidemo/AudioFrame error\n");
        return;
    }
#if 0 //
    voice_static_method = env->GetStaticMethodID(clazz,"TtReceiveVoiceByteFromJNI","(I[B)V");
#else
    voice_static_method = env->GetStaticMethodID(clazz,"TtReceiveVoiceShortFromJNI", "(I[S)V");
#endif
    if(voice_static_method == NULL){
        LOGD("jni-ndk 找不到TtReceiveVoiceByteFromJNI方法");
    }

    //add test --
//获取jbyteArray yuv420sp的数组的地址
    jbyte *yuv = env->GetByteArrayElements(yuv420sp, &isCopy);
    if(yuv == NULL){
        return;
    }

    len = env->GetArrayLength(yuv420sp);
    LOGD("jni-ndk jni YUVtoRBG getyuv420sp jbyteArray len:%d \n",len);

    for(k=0; k < len ; k++){
        LOGD("jni-ndk jni jbyteArray yuv420sp[%d] = 0x%x\n",k,yuv[k]);
    }

//add test ++
#if  0
    voice_to_java = env->NewByteArray(len);
    if(voice_to_java == NULL){
        LOGD("jni-ndk NewByteArray voice_to_java error");
        return;
    }

    env->SetByteArrayRegion(voice_to_java, 0, len, yuv);
    if(env->ExceptionCheck()){
        LOGD("jni-ndk SetByteArrayRegion error");
        goto error;
    }
#endif
    LOGD("jni-ndk NewShortArray");
    voice_short_data = env->NewShortArray(short_size);
    if(voice_short_data == NULL){
        LOGD("jni-ndk NewByteArray voice_short_data error");
        return;
    }
    LOGD("jni-ndk SetShortArrayRegion");
    env->SetShortArrayRegion(voice_short_data, 0, short_size,voice_short_to_java);
#if  0
    env->CallStaticVoidMethod(clazz,voice_static_method,len,voice_to_java);
#else
    LOGD("jni-ndk CallStaticVoidMethod TtReceiveVoiceShortFromJNI");
    env->CallStaticVoidMethod(clazz,voice_static_method,short_size ,voice_short_data);
#endif
    if(env->ExceptionCheck()){
        LOGD("jni-ndk CallStaticVoidMethod error");
        goto error;
    }
//add test --

    LOGD("jni-ndk ReleaseByteArrayElements");
    env->ReleaseByteArrayElements(yuv420sp,yuv,JNI_ABORT);
    goto clean_and_return;

error:
    LOGD("jni-ndk Failed and Exception error");
    env->ExceptionClear();

clean_and_return:
    if(voice_short_data != NULL){
        LOGD("jni-ndk DeleteLocalRef voice_short_data");
        env->DeleteLocalRef(voice_short_data);
    }
    if(voice_to_java != NULL){
        LOGD("jni-ndk DeleteLocalRef voice_to_java");
        env->DeleteLocalRef(voice_to_java);
    }
    LOGD("jni-ndk DeleteLocalRef clazz");
    env->DeleteLocalRef(clazz);
    LOGD("jni-ndk YUVtoRBG ok");
    return;

}

static pthread_t tt_audio_play;
static pthread_t tt_audio_record;
static bool play = false;
JavaVM* java_vm = NULL;
pthread_attr_t pthreadAttr;//线程属性

void *tt_audio_play_thread(void *args){
    JNIEnv *env = NULL;
    int m = 0;
    if(java_vm->AttachCurrentThread(&env, NULL) == 0) {
        LOGD("jni-ndk AttachCurrentThread success");
        while (play) {
            /*
        //if (play) {
            m++;
            sleep(1);
            LOGD("jni-ndk in tt_audio_play_thread :m=%d\n",m);
        }
        */

        java_vm->DetachCurrentThread();
    }
    LOGD("jni-ndk in tt_audio_play_thread end\n");
    //pthread_join(tt_audio_play, NULL);
    return NULL;
}


    JNIEXPORT extern "C" jint JNICALL Java_com_android_test_jnidemo_JniText_tianTongAudioStart
(JNIEnv *env, jclass obj)
{
    LOGD("jni-ndk tianTongAudioStart");
    audio_init();
    /*
    thread_param_t* param = (thread_param_t*) malloc(sizeof(thread_param_t));

    memset(param, 0, sizeof(thread_param_t));
    param->thread_name = (unsigned char*)malloc(255);
    memset(param->thread_name, 0, 255);
    sprintf(reinterpret_cast<char *const>(param->thread_name), "thread id:1");
    param->err = 0;
    */
    /*
    play = true;
    //if(pthread_create(&tt_audio_play, NULL, tt_audio_play_thread, (void *)param)){
    if(pthread_create(&tt_audio_play, NULL, tt_audio_play_thread, NULL)){
        LOGD("tt_audio_play_thread error");
        return 0;
    }
     */
    return 0;
}

extern "C" JNIEXPORT jint JNICALL Java_com_android_test_jnidemo_JniText_tianTongAudioStop
        (JNIEnv *env, jclass obj)
{
    LOGD("jni-ndk tianTongAudioStop");
    audio_stop();
    /*
    //sleep(3);
    play = false;
    pthread_join(tt_audio_play, NULL);
    //pthread_kill(tt_audio_play,0);
     */
    LOGD("jni-ndk tianTongAudioStop over");
    return 0;
}

extern "C" JNIEXPORT jint JNICALL Java_com_android_test_jnidemo_JniText_tianTongAudioSFromPc
        (JNIEnv *env, jclass obj, jshortArray data, jint len)
{

    return 0;
}

extern "C" JNIEXPORT jint JNICALL Java_com_android_test_jnidemo_JniText_tianTongAudioBFromPc
        (JNIEnv *env, jclass obj, jbyteArray data, jint len)
{
    LOGD("jni-ndk tianTongAudioBFromPc data len:%d",len);
    int ret;
    jboolean isCopy;
    char *sendPtr;
    jbyte *voice_audio = env->GetByteArrayElements(data, &isCopy);

    if(voice_audio == NULL){
        LOGD("jni-ndk tianTongAudioBFromPc GetByteArrayElements error ");
    }

    sendPtr = (char *)voice_audio;
#if DEBUG_LOG
    for(int i = 0; i < len; i++){
        LOGD("jni-ndk tianTongAudioBFromPc sendPtr[%d] = 0x%x\n",i,sendPtr[i]);
    }
#endif
    ret = send_audio_to_tt(sendPtr, len);
    if(ret != 0){
        LOGD("jni-ndk send_audio_to_tt err:%d\n",ret);
    }

    LOGD("jni-ndk tianTongAudioBFromPc ok \n");

    env->ReleaseByteArrayElements(data, voice_audio);

    return ret;
}

extern "C" JNIEXPORT jint JNICALL Java_com_android_test_jnidemo_JniText_startRecieveTianTongAudio
        (JNIEnv *env, jclass obj)
{

    play = true;
    if(pthread_create(&tt_audio_play, NULL, tt_audio_play_thread, NULL)){
        LOGD("tt_audio_play_thread error");
        play = false;
        return -1;
    }
    return 0;
}

extern "C" JNIEXPORT jint JNICALL Java_com_android_test_jnidemo_JniText_stopRecieveTianTongAudio
        (JNIEnv *env, jclass obj)
{
    if(play == true) {
        play = false;
        pthread_join(tt_audio_play, NULL);
    }
    return 0;
}

// so库加载时调用
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv *env;

    LOGD("jni-ndk jni JNI_OnLoad");

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    {
        LOGD("jni-ndk jni JavaVM error");
        return -1;
    }

    java_vm = vm;
    LOGD("jni-ndk jni JNI_OnLoad ok");
    return JNI_VERSION_1_4;
}